package com.progenies.ecg.asm31.generator;

import java.io.PrintWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.ASMifierClassVisitor;
import org.objectweb.asm.util.CheckClassAdapter;

import com.progenies.ecg.asm31.model.codeparts.IConfigurableClassObject;
import com.progenies.ecg.asm31.model.codeparts.lines.LineFactoryASM31;
import com.progenies.ecg.asm31.model.factory.ObjectFactoryASM31;
import com.progenies.ecg.generator.ByteArrayClassLoader;
import com.progenies.ecg.generator.ClassGenerator;
import com.progenies.ecg.model.ClassObject;
import com.progenies.ecg.model.codeparts.lines.LineFactory;
import com.progenies.ecg.model.factory.ObjectFactory;

/**
 * Main utility of the library, it will receive an ECG model and will generate a java Class object internally.
 * 
 * Checking while generation is enabled. The code version will be automatically calculated from the current JVM, being the minimal version v1.4
 * 
 * @author ofc587a87
 *
 */
public final class ClassGeneratorASM31 extends ClassGenerator
{
	private boolean showCompilation=false;
	
	private ByteArrayClassLoader classLoader;

	public ClassGeneratorASM31()
	{
		classLoader=AccessController.doPrivileged(new PrivilegedAction<ByteArrayClassLoader>() {

			@Override
			public ByteArrayClassLoader run() {
				return new ByteArrayClassLoader(getClass().getClassLoader());
			}
		});
		
	}
	
	
	
	public final Class<?> generateClass(final ClassObject classObject)
	{
		if(!(classObject instanceof IConfigurableClassObject))
		{
			throw new IllegalArgumentException("This class generation only support ASM3.1 classes, the paramater was "+classObject.getClass().getName());
		}
		IConfigurableClassObject clsObject=(IConfigurableClassObject) classObject;
		
		ClassWriter writer=new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
		ClassVisitor visitor=new CheckClassAdapter(writer);
//		ClassVisitor visitor=writer;
		
		//Step 1: class configuration
		clsObject.configure(getJavaVersion(), visitor);
		
		//Step 2: class generation
		clsObject.generateBytecode();
		
		if(showCompilation)
		{
			ClassReader reader=new ClassReader(writer.toByteArray());
			reader.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), ClassReader.EXPAND_FRAMES | ClassReader.SKIP_DEBUG);
		}
		
		//Step 3: Class loading and Done!
		return classLoader.loadByteArray(writer.toByteArray());
	}
	
	
	
	private static final int getJavaVersion()
	{
		String jvm=System.getProperty("java.version");
		
		if(jvm==null || jvm.startsWith("1.4"))
			return Opcodes.V1_4;
		else if(jvm.startsWith("1.5"))
			return Opcodes.V1_5;
		else if(jvm.startsWith("1.6") || jvm.startsWith("1.7"))
			return Opcodes.V1_6;
//		else if(jvm.startsWith("1.7")) // ASM 3.3.1 doesn't support v1.7
//			return Opcodes.V1_7;
		else
			return Opcodes.V1_4;
	}



	@Override
	protected final LineFactory createLineFactory() {
		return new LineFactoryASM31();
	}



	@Override
	protected final ObjectFactory createObjectFactory() {
		return new ObjectFactoryASM31();
	}



}
